﻿//
// *************************************************************************************
// WP-Framework
// 
// Developed by
// Lukas Kretschmar (lkretschmar.dev@bluewin.ch)
//
// Switzerland, Copyright (c) 2013 lkretschmar
// This content is released under the MIT License (http://opensource.org/licenses/MIT).
//     
// *************************************************************************************
//
//
			

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using KretschIT.WP_Fx.Core.Validation;
using KretschIT.WP_Fx.EasyMoq.Generator.Constraints;
using KretschIT.WP_Fx.EasyMoq.Generator.Arguments;
using KretschIT.WP_Fx.EasyMoq.Generator.Items;

namespace KretschIT.WP_Fx.EasyMoq.Generator
{
    public class EasyMoqClassTemplateHelper : IEasyMoqClassTemplateHelper
    {
        private const string VoidTypeName = "void";
        private const string EventRaisePrefix = "Raise";
        private const string EventRaiseSuffix = "Event";
        private const string EventDelegateMethod = "Invoke";

        private readonly IClassAnalyzer analyzer = new ClassAnalyzer();
        private readonly INamingFactory nameFactory;

        public EasyMoqClassTemplateHelper(INamingFactory nameFactory)
        {
            It.IsNotNull(() => nameFactory);

            this.nameFactory = nameFactory;
        }

        #region Get

        #region Events

        public IEnumerable<EventInfo> GetEventsOf(Type type)
        {
            return this.analyzer.GetEventsOf(type);
        }

        #endregion

        #region Methods

        public IEnumerable<MethodInfo> GetMethodsOf(Type type)
        {
            return this.analyzer.GetMethodsOf(type);
        }

        #endregion

        #region Properties

        public IEnumerable<PropertyInfo> GetPropertiesOf(Type type)
        {
            return this.analyzer.GetPropertiesOf(type);
        }

        #endregion

        private IEnumerable<T> Collect<T>(Type type, Func<Type, IEnumerable<T>> collectOf, Func<IEnumerable<T>, T, bool> isContainedIn, Action<List<T>, IEnumerable<T>> addTo = null)
        {
            var list = new List<T>();
            list.AddRange(collectOf(type));
            foreach (var i in type.GetInterfaces())
            {
                var newParts = this.FilterNew<T>(collectOf, isContainedIn, list, i);
                this.AddTo(list, newParts, addTo);
            }
            return list;
        }

        private IEnumerable<T> FilterNew<T>(Func<Type, IEnumerable<T>> collectOf, Func<IEnumerable<T>, T, bool> isContainedIn, List<T> list, Type i)
        {
            var parts = collectOf(i);
            return parts.Where(p => !isContainedIn(list, p));
        }

        private void AddTo<T>(List<T> list, IEnumerable<T> newParts, Action<List<T>, IEnumerable<T>> addTo)
        {
            if (addTo != null)
            {
                addTo(list, newParts);
            }
            else
            {
                list.AddRange(newParts);
            }
        }

        #endregion

        #region ReturnType

        public string GetReturnType(Type type)
        {
            if (this.HasReturnType(type))
            {
                return this.GetUniqueNameOf(type) + this.BuildGenerics(type.GetGenericArguments());
            }
            return VoidTypeName;
        }

        public bool HasReturnType(Type type)
        {
            It.IsNotNull(() => type);

            return type != typeof(void);
        }

        #endregion

        #region UniqueName
        
        public string GetUniqueNameOf(Type type)
        {
            return this.nameFactory.GetNormalizedNameOf(type);
        }

        #endregion

        #region Arguments

        public string CreateArguments(IEnumerable<ParameterInfo> arguments, bool includeType)
        {
            It.IsNotNull(() => arguments);

            IParameterArgumentBuilder builder = new ParameterArgumentBuilder(this.nameFactory);
            foreach (var p in arguments)
            {
                builder.Add(p, includeType);
            }
            return builder.String;
        }
        
        #endregion

        #region Generics

        #region Class

        public string CreateGenericArgumentsFor(Type type)
        {
            It.IsNotNull(() => type);

            if (type.IsGenericType)
            {
                var t = type.GetGenericTypeDefinition();
                return this.BuildGenerics(t.GetGenericArguments());
            }
            return string.Empty;
        }

        #endregion

        #region Events

        public string CreateGenericArgumentsFor(EventInfo e)
        {
            It.IsNotNull(() => e);
            return this.BuildGenerics(e.EventHandlerType.GetGenericArguments());
        }

        #endregion

        #region Methods

        public string CreateGenericArgumentsFor(MethodInfo method)
        {
            It.IsNotNull(() => method);

            return this.BuildGenerics(method.GetGenericArguments());
        }

        #endregion

        #region Constraints

        public string GetGenericConstraintOf(IEnumerable<Type> genericArguments)
        {
            IGenericConstraintBuilder builder = new GenericConstraintBuilder(this.nameFactory);
            foreach (var t in genericArguments)
            {
                builder.AddGenericType(t);
            }
            return builder.GenericConstraint;
        }

        #endregion

        #region Types

        public string CreateGenericArgumentsWith(IEnumerable<Type> types)
        {
            It.IsNotNull(() => types);

            return this.BuildGenerics(types);
        }

        #endregion

        private string BuildGenerics(IEnumerable<Type> types)
        {
            return GenericsHelper.BuildGenerics(types, this.nameFactory);
        }      

        #endregion           

        #region Events

        public string  GetRaiseMethodOf(EventInfo e)
        {
            return EventRaisePrefix + e.Name + EventRaiseSuffix;
        }

        public MethodInfo GetSignatureOf(EventInfo e)
        {
            return e.EventHandlerType.GetMethod(EventDelegateMethod);
        }

        #endregion
    }
}
